package com.srmt.feishu.client.impl;

import com.lark.oapi.Client;
import com.lark.oapi.service.docx.v1.model.*;
import com.lark.oapi.service.drive.v1.model.*;
import com.srmt.feishu.client.FeishuClient;
import com.srmt.feishu.client.DocxService;
import com.srmt.feishu.client.DriveService;
import com.srmt.feishu.client.SheetsService;
import com.srmt.common.retry.RetryHandler;
import com.srmt.common.retry.RetryConfig;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 支持429自动重试的飞书客户端实现
 * 在所有API调用上应用指数退避重试机制
 * 
 * @author SRMT Development Team
 * @since 1.0.0
 */
public class RetryableFeishuClient implements FeishuClient {
    
    private static final Logger logger = LoggerFactory.getLogger(RetryableFeishuClient.class);
    
    private final FeishuClient delegate;
    private final RetryHandler retryHandler;
    private final RetryableDocxService docxService;
    private final RetryableDriveService driveService;
    private final RetryableSheetsService sheetsService;
    
    /**
     * 构造函数
     *
     * @param delegate 被包装的飞书客户端
     * @param retryConfig 重试配置
     */
    public RetryableFeishuClient(FeishuClient delegate, RetryConfig retryConfig) {
        if (delegate == null) {
            throw new IllegalArgumentException("委托客户端不能为null");
        }
        if (retryConfig == null) {
            throw new IllegalArgumentException("重试配置对象不能为null");
        }
        
        this.delegate = delegate;
        this.retryHandler = new RetryHandler(retryConfig);

        this.docxService = new RetryableDocxService(delegate.docx(), retryHandler);
        this.driveService = new RetryableDriveService(delegate.drive(), retryHandler);
        this.sheetsService = new RetryableSheetsService(delegate.sheets(), retryHandler);

        logger.info("可重试飞书客户端已初始化,重试配置: {}", retryConfig);
    }
    
    @Override
    public DocxService docx() {
        return docxService;
    }
    
    @Override
    public DriveService drive() {
        return driveService;
    }

    @Override
    public SheetsService sheets() {
        return sheetsService;
    }
    
    @Override
    public void shutdown() {
        logger.info("正在关闭可重试飞书客户端");
        delegate.shutdown();
    }

    /**
     * 支持重试的文档服务实现
     */
    private static class RetryableDocxService implements DocxService {
        private static final Logger logger = LoggerFactory.getLogger(RetryableDocxService.class);
        
        private final DocxService delegate;
        private final RetryHandler retryHandler;
        
        RetryableDocxService(DocxService delegate, RetryHandler retryHandler) {
            this.delegate = delegate;
            this.retryHandler = retryHandler;
        }
        
        @Override
        public Document createDocument(CreateDocumentReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.createDocument(req),
                RetryHandler::isRateLimitException,
                "docxDocumentCreate"
            );
        }

        @Override
        public Document getDocument(GetDocumentReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.getDocument(req),
                RetryHandler::isRateLimitException,
                "docxDocumentGet"
            );
        }

        @Override
        public Block getDocumentBlock(GetDocumentBlockReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.getDocumentBlock(req),
                RetryHandler::isRateLimitException,
                "docxDocumentBlockGet"
            );
        }

        @Override
        public Block[] listDocumentBlock(ListDocumentBlockReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.listDocumentBlock(req),
                RetryHandler::isRateLimitException,
                "docxDocumentBlockList"
            );
        }

        @Override
        public java.util.List<Block> listAllDocumentBlocks(String documentId) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.listAllDocumentBlocks(documentId),
                RetryHandler::isRateLimitException,
                "docxDocumentBlockListAll"
            );
        }

        @Override
        public Block patchDocumentBlock(PatchDocumentBlockReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.patchDocumentBlock(req),
                RetryHandler::isRateLimitException,
                "docxDocumentBlockPatch"
            );
        }

        @Override
        public CreateDocumentBlockChildrenRespBody createDocumentBlockChildren(CreateDocumentBlockChildrenReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.createDocumentBlockChildren(req),
                RetryHandler::isRateLimitException,
                "docxDocumentBlockChildrenCreate"
            );
        }

        @Override
        public CreateDocumentBlockDescendantRespBody createDocumentBlockDescendant(CreateDocumentBlockDescendantReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.createDocumentBlockDescendant(req),
                RetryHandler::isRateLimitException,
                "docxDocumentBlockDescendantCreate"
            );
        }

        @Override
        public BatchUpdateDocumentBlockRespBody batchUpdateDocumentBlock(BatchUpdateDocumentBlockReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.batchUpdateDocumentBlock(req),
                RetryHandler::isRateLimitException,
                "docxDocumentBlockBatchUpdate"
            );
        }
    }
    
    /**
     * 支持重试的云盘服务实现
     */
    private static class RetryableDriveService implements DriveService {
        private static final Logger logger = LoggerFactory.getLogger(RetryableDriveService.class);
        
        private final DriveService delegate;
        private final RetryHandler retryHandler;
        
        RetryableDriveService(DriveService delegate, RetryHandler retryHandler) {
            this.delegate = delegate;
            this.retryHandler = retryHandler;
        }
        
        @Override
        public String copyFile(CopyFileReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.copyFile(req),
                RetryHandler::isRateLimitException,
                "driveFileCopy"
            );
        }

        @Override
        public void deleteFile(DeleteFileReq req) throws Exception {
            retryHandler.executeWithRetry(
                () -> {
                    delegate.deleteFile(req);
                    return null;
                },
                RetryHandler::isRateLimitException,
                "driveFileDelete"
            );
        }

        @Override
        public BaseMember createPermissionMember(CreatePermissionMemberReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.createPermissionMember(req),
                RetryHandler::isRateLimitException,
                "drivePermissionMemberCreate"
            );
        }

        @Override
        public BaseMember updatePermissionMember(UpdatePermissionMemberReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.updatePermissionMember(req),
                RetryHandler::isRateLimitException,
                "drivePermissionMemberUpdate"
            );
        }

        @Override
        public void deletePermissionMember(DeletePermissionMemberReq req) throws Exception {
            retryHandler.executeWithRetry(
                () -> {
                    delegate.deletePermissionMember(req);
                    return null;
                },
                RetryHandler::isRateLimitException,
                "drivePermissionMemberDelete"
            );
        }

        @Override
        public Member[] listPermissionMember(ListPermissionMemberReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.listPermissionMember(req),
                RetryHandler::isRateLimitException,
                "drivePermissionMemberList"
            );
        }
    }

    /**
     * 支持重试的电子表格服务实现
     */
    private static class RetryableSheetsService implements SheetsService {
        private static final Logger logger = LoggerFactory.getLogger(RetryableSheetsService.class);

        private final SheetsService delegate;
        private final RetryHandler retryHandler;

        RetryableSheetsService(SheetsService delegate, RetryHandler retryHandler) {
            this.delegate = delegate;
            this.retryHandler = retryHandler;
        }

        // ========== 基础信息查询 (v3 API - 已重构) ==========

        @Override
        public com.lark.oapi.service.sheets.v3.model.Sheet getSheet(
                String spreadsheetToken, String sheetId) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.getSheet(spreadsheetToken, sheetId),
                RetryHandler::isRateLimitException,
                "sheetsSpreadsheetSheetGet"
            );
        }

        @Override
        public com.lark.oapi.service.sheets.v3.model.GetSpreadsheet getSpreadsheet(
                String spreadsheetToken) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.getSpreadsheet(spreadsheetToken),
                RetryHandler::isRateLimitException,
                "sheetsSpreadsheetGet"
            );
        }

        @Override
        public com.lark.oapi.service.sheets.v3.model.Sheet[] querySheets(
                String spreadsheetToken) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.querySheets(spreadsheetToken),
                RetryHandler::isRateLimitException,
                "sheetsSpreadsheetSheetQuery"
            );
        }

        @Override
        public com.lark.oapi.service.sheets.v3.model.Spreadsheet createSpreadsheet(
                com.lark.oapi.service.sheets.v3.model.CreateSpreadsheetReq req) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.createSpreadsheet(req),
                RetryHandler::isRateLimitException,
                "sheetsSpreadsheetCreate"
            );
        }

        // ========== 数据读写操作 ==========

        @Override
        public java.util.List<java.util.List<String>> getValues(
                String spreadsheetToken, String range) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.getValues(spreadsheetToken, range),
                RetryHandler::isRateLimitException,
                "sheetsValuesGet"
            );
        }

        @Override
        public boolean updateValues(String spreadsheetToken, String range,
                                   java.util.List<java.util.List<String>> values) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.updateValues(spreadsheetToken, range, values),
                RetryHandler::isRateLimitException,
                "sheetsValuesUpdate"
            );
        }

        @Override
        public boolean batchUpdateValues(String spreadsheetToken,
                                        java.util.Map<String, java.util.List<java.util.List<String>>> updates)
                                        throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.batchUpdateValues(spreadsheetToken, updates),
                RetryHandler::isRateLimitException,
                "sheetsValuesBatchUpdate"
            );
        }

        @Override
        public boolean prependValues(String spreadsheetToken, String range,
                                    java.util.List<java.util.List<String>> values) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.prependValues(spreadsheetToken, range, values),
                RetryHandler::isRateLimitException,
                "sheetsValuesPrepend"
            );
        }

        @Override
        public boolean appendValues(String spreadsheetToken, String range,
                                   java.util.List<java.util.List<String>> values) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.appendValues(spreadsheetToken, range, values),
                RetryHandler::isRateLimitException,
                "sheetsValuesAppend"
            );
        }

        // ========== 维度操作（行列） ==========

        @Override
        public boolean addDimensionRange(String spreadsheetToken, String sheetId,
                                        String dimension, int length) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.addDimensionRange(spreadsheetToken, sheetId, dimension, length),
                RetryHandler::isRateLimitException,
                "sheetsDimensionRangeAdd"
            );
        }

        @Override
        public boolean insertDimensionRange(String spreadsheetToken, String sheetId,
                                           String dimension, int startIndex, int endIndex) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.insertDimensionRange(spreadsheetToken, sheetId, dimension, startIndex, endIndex),
                RetryHandler::isRateLimitException,
                "sheetsDimensionRangeInsert"
            );
        }

        @Override
        public boolean deleteDimensionRange(String spreadsheetToken, String sheetId,
                                           String dimension, int startIndex, int endIndex) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.deleteDimensionRange(spreadsheetToken, sheetId, dimension, startIndex, endIndex),
                RetryHandler::isRateLimitException,
                "sheetsDimensionRangeDelete"
            );
        }

        @Override
        public boolean updateDimensionRange(String spreadsheetToken, String sheetId,
                                           String dimension, int startIndex, int endIndex, int size)
                                           throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.updateDimensionRange(spreadsheetToken, sheetId, dimension, startIndex, endIndex, size),
                RetryHandler::isRateLimitException,
                "sheetsDimensionRangeUpdate"
            );
        }

        // ========== 单元格操作 ==========

        @Override
        public boolean mergeCells(String spreadsheetToken, String sheetId,
                                 int startRow, int startCol, int endRow, int endCol) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.mergeCells(spreadsheetToken, sheetId, startRow, startCol, endRow, endCol),
                RetryHandler::isRateLimitException,
                "sheetsMergeCells"
            );
        }

        @Override
        public boolean unmergeCells(String spreadsheetToken, String sheetId,
                                   int startRow, int startCol, int endRow, int endCol) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.unmergeCells(spreadsheetToken, sheetId, startRow, startCol, endRow, endCol),
                RetryHandler::isRateLimitException,
                "sheetsUnmergeCells"
            );
        }

        @Override
        public boolean setCellStyle(String spreadsheetToken, String range, Object style) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.setCellStyle(spreadsheetToken, range, style),
                RetryHandler::isRateLimitException,
                "sheetsCellStyleSet"
            );
        }

        @Override
        public boolean setDataValidation(String spreadsheetToken, String range, Object validation) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.setDataValidation(spreadsheetToken, range, validation),
                RetryHandler::isRateLimitException,
                "sheetsDataValidationSet"
            );
        }

        // ========== 保护范围操作 ==========

        @Override
        public boolean addProtectedDimension(String spreadsheetToken,
                                            java.util.List<com.srmt.feishu.client.model.ProtectedDimension> protectedDimensions,
                                            String userIdType) throws Exception {
            return retryHandler.executeWithRetry(
                () -> delegate.addProtectedDimension(spreadsheetToken, protectedDimensions, userIdType),
                RetryHandler::isRateLimitException,
                "sheetsProtectedDimensionAdd"
            );
        }
    }

}